/** GDS:
- reversed tweens don't reinit after delay (because they've already shifted to their end position)
- reversed needs to be set after all properties, because it immediately sets position to avoid flickering.
- rewrite ticker logic to make it more publicly accessible?
- add clone back in?
- finish prep for player 10.
**/

/**
* GTween by Grant Skinner. Aug 15, 2008
* Visit www.gskinner.com/blog for documentation, updates and more free code.
*
*
* Copyright (c) 2008 Grant Skinner
* 
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
* 
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
**/

package com.gskinner.motion {
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	
	
	/**
	* This event is dispatched each time the tween updates properties on its target.
	* It will be dispatched each "tick" during the TWEEN.
	*
	* @eventType flash.events.Event
	**/
	[Event(name="change", type="flash.events.Event")]
	/**
	* Dispatched when a tween copies its initial properties and starts tweening. In tweens with a delay of 0, this event will
	* fire immediately when it starts playing. In tweens with a delay set, this will fire when the delay state is ended, and the tween state is entered.
	*
	* @eventType flash.events.Event
	**/
	[Event(name="init", type="flash.events.Event")]
	/**
	* Dispatched when a tween ends (its position equals its duration).
	*
	* @eventType flash.events.Event
	**/
	[Event(name="complete", type="flash.events.Event")]
	
	/**
	* <b>GTween ©2008 Grant Skinner, gskinner.com. Visit www.gskinner.com/libraries/gtween/ for documentation, updates and more free code. Licensed under the MIT license - see the source file header for more information.</b>
	* <hr>
	* GTween is a light-weight instance oriented tween engine. This means that you instantiate tweens for specific purposes, and then reuse, update or discard them.
	* This is different than centralized tween engines where you "register" tweens with a global object. This provides a more familiar and useful interface
	* for object oriented programmers.
	* <br/><br/>
	* In addition to a more traditional setProperty/setProperties tweening interface, GTween also provides a unique proxy interface to tween and access properties of target objects
	* in a more dynamic fashion. This allows you to work with properties directly, and mostly ignore the details of the tween. The proxy "stands in"
	* for your object when working with tweened properties. For example, you can modify end values (the value you are tweening to), in the middle of a tween.
	* You can also access them dynamically, like so:
	* <br/><br/>
	* <code>mySpriteTween.proxy.rotation += 50;</code>
	* <br/><br/>
	* Assuming no end value has been set for rotation previously, the above example will get the current rotation from the target, add 50 to it, set it as the end
	* value for rotation, and start the tween. If the tween has already started, it will adjust for the new values. This is a hugely powerful feature that
	* requires a bit of exploring to completely understand. See the documentation for the "proxy" property for more information.
	* <br/><br/>
	* For a light-weight engine (<4.5kb), GTween boasts a number of advanced features:<UL>
	* <LI> three timing modes: frame based, time based, and hybrid
	* <LI> works with any numeric properties on any object (not just display objects)
	* <LI> sequenced tweens using .nextTween
	* <LI> pause and resume individual tweens or all tweens
	* <LI> jump directly to the end or beginning of a tween with .end() or .beginning()
	* <LI> jump to any arbitrary point in the tween with .position
	* <LI> auto hide for alpha tweens, sets visible to false when alpha is 0 or less with .autoVisible set to true
	* <LI> smart tweening for rotation (rotates in the shortest direction) with .autoRotation and .rotationProperties
	* <LI> property snapping (rounding) with .snapping and .snappingProperties
	* <LI> complete, init, and change events
	* <LI> smart garbage collector interactions (prevents collection while active, allows collection if target is collected)
	* <LI> uses any standard ActionScript tween functions
	* <LI> support for tweening objects like colorTransform and matrix that need to be reassigned to a property
	* <LI> easy to set up in a single line of code
	* <LI> can repeat or reflect a tween specified number of times
	* <LI> .reverse() method will reverse a linear tween seamlessly, respecting repeat and reflect
	* <LI> deterministic, so setting a position on a tween will (almost) always result in predictable results
	* <LI> very powerful sequencing capabilities in conjunction with GTweenTimeline.
	* </UL>
	* Also see GTweenFilter and GTweenTimeline for more advanced functionality, or GTweensy for a more lightweight engine.
	*
	* <hr/>
	* <b>Beta 5 updates (Feb 1, 2009):</b><br/>
	* Beta 5 constitutes a major rewrite of GTween. Much of the API has been changed, and it is recommended that you read the API documents
	* to familiarize yourself with the new capabilities. The following list might not be exhaustive.
	* <ul>
	* <li> low level rewrite of positioning logic, to make position handling fully deterministic
	* <li> removed .clone();
	* <li> removed .useSetSize and related functionality
	* <li> removed activate event
	* <li> removed .addProgressPoint functionality (use GTweenTimeline with .addCallback instead)
	* <li> removed .addChild and related functionality (use GTweenTimeline with .addTween instead)
	* <li> removed .assignmentTarget and .assignmentProperty in favour of .setAssignment()
	* <li> removed .autoReverse property, use repeat and reflect instead
	* <li> renamed BEGINNING, DELAY_PHASE, and TWEEN_PHASE to START, DELAY, and TWEEN respectively
	* <li> renamed .useSmartRotation to .autoRotation
	* <li> renamed .autoHide to .autoVisible
	* <li> renamed .useSnapping to .snapping
	* <li> added .repeat property, which lets you specify the number of times the tween should repeat
	* <li> added .reflect, which causes the tween to play backwards when it repeats
	* <li> added .reversed property, which causes the tween to play backwards
	* <li> added .getProperties(), which returns a hash table of end values
	* <li> added .setStartProperties(), allows you to manually specify the start values
	* <li> added .getStartProperties(), which returns the start values
	* <li> added .lockStartProperties, which prevents start values from re-initing
	* <li> added .tweenPosition, which provides a calculate position between 0 and duration
	* <li> added .setPosition, which allows you to specify whether to suppress events when manually setting position
	* <li> the .state property is now calculated on demand
	* <li> reduced size to under 4.5kb for GTween
	* </ul>
	**/
	
	public class GTween extends EventDispatcher {
		
	// static interface:
		/** @private **/
		protected static var _timingMode:String;
		/** @private **/
		protected static var _timeInterval:uint = 40;
		/** @private **/
		protected static var _activeTicker:ITicker;
		/** @private **/
		protected static var activeTweens:Dictionary = new Dictionary(); // keeps active tweens in memory
	
		/** Constant for the TIME timingMode. **/
		public static const TIME:String = "time";
		/** Constant for the FRAME timingMode. **/
		public static const FRAME:String = "frame";
		/** Constant for the HYBRID timingMode. **/
		public static const HYBRID:String = "hybrid";
		/** Constant for the START state. **/
		public static const START:String = "start";
		/** Constant for the DELAY state. **/
		public static const DELAY:String = "delay";
		/** Constant for the TWEEN state. **/
		public static const TWEEN:String = "tween";
		/** Constant for the END state. **/
		public static const END:String = "end";
		
		/** Setting this to true pauses all tween instances. This does not affect individual tweens' .paused property. **/
		public static var pauseAll:Boolean=false;
		
		/** Specifies the default easing function to use with new tweens. If null, GTween.linearEase will be used. **/
		public static var defaultEase:Function;
		
		/** A hash table specifying properties that should be affected by autoRotation. **/
		public static var rotationProperties:Object = {rotation:true,rotationX:true,rotationY:true,rotationZ:true};
		
		/** A hash table specifying properties that should have their value rounded (snapped) before being applied. This can be toggled for each tween instance basis with the .snapping property. **/
		public static var snappingProperties:Object = {x:true,y:true};
		
		/**
		* Indicates how GTween should deal with timing. This can be set to GTween.TIME, GTween.FRAME, or GTween.HYBRID.
		* <br/><br/>
		* In frame mode, GTween will update once every frame, and all positional values are specified in frames (duration, position, delay, etc).
		* <br/><br/>
		* In time mode, updates will occur at an interval specified by the timeInterval property, independent of the frame rate, and all positional values are
		* specified in seconds.
		* <br/><br/>
		* In hybrid mode, all updates occur on a frame, but all positional values are specified in seconds. Each frame the tween will calculate it's position based on the elapsed time.
		* This offers lower CPU usage, and a more familiar time-based interface, but can result in choppy animations in high CPU situations.
		* <br/><br/>
		* The hybrid mode generally provides the smoothest results. The frame mode makes it easy to synch tweens with timeline animations.
		* You can change modes at any time, but existing tweens will continue to use the mode that was active when they were created.
		**/
		public static function get timingMode():String {
			return _timingMode;
		}
		public static function set timingMode(value:String):void {
			value = (value == FRAME || value == TIME) ? value : HYBRID;
			if (value == _timingMode) { return; }
			_timingMode = value;
			if (_timingMode == TIME) {
				_activeTicker = new TimeTicker();
				(_activeTicker as TimeTicker).interval = _timeInterval/1000;
			} else if (_timingMode == FRAME) {
				_activeTicker = new FrameTicker();
			} else {
				_activeTicker = new HybridTicker();
			}
		}
		
		/**
		* Sets the time in milliseconds between updates when timingMode is set to GTween.TIME ("time"). Setting this to a lower number
		* will generally result in smoother animations but higher CPU usage. Defaults to 40ms (~25 updates per second).
		**/
		public static function get timeInterval():uint {
			return _timeInterval;
		}
		public static function set timeInterval(value:uint):void {
			_timeInterval = value;
			if (_activeTicker is TimeTicker) { (_activeTicker as TimeTicker).interval = _timeInterval/1000; }
		}
		
		/** The currently active Ticker object. **/
		public static function get activeTicker():ITicker {
			if (_timingMode == null) { timingMode = HYBRID; }
			return _activeTicker;
		}
		
		/** The default easing function used by GTween. **/
		public static function linearEase(t:Number, b:Number, c:Number, d:Number):Number {
			return t;
		}
	// END static interface.
	
	
	// public properties:
		/**
		* Indicates whether the tween should automatically play when an end value is changed.
		**/
		public var autoPlay:Boolean = true;
		
		/**
		* When true, the tween will always rotate in the shortest direction to reach an end rotation value.
		* For example, rotating from 355 degress to 5 degrees will rotate 10 degrees clockwise with .autoRotation set to true.
		* It would rotate 350 degrees counter-clockwise with .autoRotation set to false. This affects all properties specified
		* in the static .rotationProperties hash table.
		**/
		public var autoRotation:Boolean = false;
		
		/**
		* Indicates whether the target's visible property should automatically be set to false when its alpha value is tweened to 0 or less.
		* Only affects objects with a visible property.
		**/
		public var autoVisible:Boolean = true;
		
		/**
		* Allows you to associate arbitrary data with your tween. For example, you might use this to reference specific data when handling events from tweens.
		**/
		public var data:*;
		
		/**
		* The length of the tween in frames or seconds (depending on the timingMode). Setting this will also update any child transitions
		* that have synchDuration set to true.
		**/
		public var duration:Number=1;
		
		/**
		* The easing function to use for calculating the tween. This can be any standard tween function, such as the tween functions in fl.motion.easing.* that come with Flash CS3.
		* New tweens will have this set to the defaultTween. Setting this to null will cause GTween to throw null reference errors.
		**/
		public var ease:Function=linearEase;
		
		/**
		* Specifies another GTween instance that will have paused=false called on it when this tween completes.
		**/
		public var nextTween:GTween;
		
		/**
		* Indicates whether the tween should use the reflect mode when repeating. If reflect is set to true, then the tween will play backwards on every other repeat.
		* This has similar effects to reversed, but the properties are exclusive of one another.
		* For instance, with reversed set to false, reflected set to true, and a repeat of 1, the tween will play from start to end, then repeat and reflect to play from end to start.
		* If in the previous example reversed was instead set to true, the tween would play from end to start, then repeat and reflect to play from start to end.
		* Finally, with reversed set to false, reflected set to false, and a repeat of 1, the tween will play from start to end, then repeat from start to end
		**/
		public var reflect:Boolean=false;
		
		/**
		* The number of times this tween will repeat. If 0, the tween will only run once. If 1 or more, the tween will repeat that many times. If -1, the tween will repeat forever.
		**/
		public var repeat:int=0;
		
		/**
		* If set to true, tweened values specified by snappingProperties will be rounded (snapped) before they are assigned to the target.
		**/
		public var snapping:Boolean = false;
		
		
	// private properties:
		/** @private **/
		protected var startValues:Object;
		/** @private **/
		protected var endValues:Object;
		/** @private **/
		protected var inited:Boolean;
		/** @private **/
		protected var inTick:Boolean;
		/** @private **/
		protected var ticker:ITicker;
		/** @private **/
		protected var positionOffset:Number;
		/** @private **/
		protected var assignmentTarget:Object;
		/** @private **/
		protected var assignmentProperty:String;
		/** @private **/
		protected var _position:Number=0;
		/** @private **/
		protected var _previousPosition:Number;
		/** @private **/
		protected var _tweenPosition:Number=0;
		/** @private **/
		protected var _previousTweenPosition:Number;
		/** @private **/
		protected var _target:Object;
		/** @private **/
		protected var _propertyTarget:Object;
		/** @private **/
		protected var _paused:Boolean=true;
		/** @private **/
		protected var _proxy:TargetProxy;
		/** @private **/
		protected var _delay:Number=0;
		/** @private **/
		protected var _reversed:Boolean;
		/** @private **/
		protected var _lockStartProperties:Boolean;
		
	// constructor:
		/**
		* Constructs a new GTween instance.
		*
		* @param target The object whose properties will be tweened. Defaults to null.
		* @param duration The length of the tween in frames or seconds depending on the timingMode. Defaults to 10.
		* @param properties An object containing end property values. For example, to tween to x=100, y=100, you could pass {x:100, y:100} as the props object.
		* @param tweenProperties An object containing properties to set on this tween. For example, you could pass {ease:myEase} to set the ease property of the new instance. This also provides a shortcut for setting up event listeners. See .setTweenProperties() for more information.
		**/
		public function GTween(target:Object=null, duration:Number=10, properties:Object=null, tweenProperties:Object=null) {
			ticker = activeTicker;
			this.target = target;
			this.duration = duration;
			ease = defaultEase || linearEase;
			setProperties(properties);
			setTweenProperties(tweenProperties);
		}
		
	// public getter / setters:
		/**
		* The proxy object allows you to work with the properties and methods of the target object directly through GTween.
		* Numeric property assignments will be used by GTween as end values. The proxy will return GTween end values
		* when they are set, or the target's property values if they are not. Delete operations on properties will result in a deleteProperty
		* call. All other property access and method execution through proxy will be passed directly to the target object.
		* <br/><br/>
		* <b>Example 1:</b> Equivalent to calling myGTween.setProperty("scaleY",2.5):<br/>
		* <code>myGTween.proxy.scaleY = 2.5;</code>
		* <br/><br/>
		* <b>Example 2:</b> Gets the current rotation value from the target object (because it hasn't been set yet on the GTween), adds 100 to it, and then
		* calls setProperty on the GTween instance with the appropriate value:<br/>
		* <code>myGTween.proxy.rotation += 100;</code>
		* <br/><br/>
		* <b>Example 3:</b> Sets an end property value (through setProperty) for scaleX, then retrieves it from GTween (because it will always return
		* end values when available):<br/>
		* <code>trace(myGTween.proxy.scaleX); // 1 (value from target, because no end value is set)<br/>
		* myGTween.proxy.scaleX = 2; // set a end value<br/>
		* trace(myGTween.proxy.scaleX); // 2 (end value from GTween)<br/>
		* trace(myGTween.target.scaleX); // 1 (current value from target)</code>
		* <br/><br/>
		* <b>Example 4:</b> Property deletions only affect end properties on GTween, not the target object:<br/>
		* <code>myGTween.proxy.rotation = 50; // set a end value<br/>
		* trace(myGTween.proxy.rotation); // 50 (end value from GTween)<br/>
		* delete(myGTween.proxy.rotation); // delete the end value<br/>
		* trace(myGTween.proxy.rotation); // 0 (current value from target)</code>
		* <br/><br/>
		* <b>Example 5:</b> Non-numeric property access is passed through to the target:<br/>
		* <code>myGTween.proxy.blendMode = "multiply"; // passes value assignment through to the target<br/>
		* trace(myGTween.target.blendMode); // "multiply" (value from target)<br/>
		* trace(myGTween.proxy.blendMode); // "multiply" (value passed through from target)</code>
		* <br/><br/>
		* <b>Example 6:</b> Method calls are passed through to target:<br/>
		* <code>myGTween.proxy.gotoAndStop(30); // gotoAndStop(30) will be called on the target</code>
		**/
		public function get proxy():Object {
			if (_proxy == null) { _proxy = new TargetProxy(this); }
			return _proxy;
		}
		
		/**
		* Gets and sets the position in the tween in frames or seconds (depending on the timingMode). This value can be any number, and will be resolved to a tweenPosition value
		* prior to being applied to the tweened values. See tweenPosition for more information.
		* <br/><br/>
		* <b>Negative values</b><br/>
		* Values below 0 will always resolve to a tweenPosition of 0. Negative values can be used to set up a delay on the tween, as the tween will have to count up to 0 before initing.
		* <br/><br/>
		* <b>Positive values</b><br/>
		* Positive values are resolved based on the duration, repeat, reflect, and reversed properties.
		**/
		public function get position():Number {
			return _position;
		}
		public function set position(value:Number):void {
			setPosition(value,true);
		}
		
		/**
		* Indicates whether the tween is currently paused. See play() and pause() for more information.
		**/
		public function get paused():Boolean {
			return _paused;
		}
		public function set paused(value:Boolean):void {
			if (value == _paused) { return; }
			_paused = value;
			if (value) {
				ticker.removeEventListener("tick",handleTick);
			} else {
				ticker.addEventListener("tick",handleTick,false,0,true);
				if (repeat != -1 && _position >= duration*(repeat+1)) { position = 0; }
				else { updatePositionOffset(); }
			}
			setGCLock(!value);
		}
		
		/**
		* Returns the calculated absolute position in the tween. This is a deterministic value between 0 and duration calculated
		* from the current position based on the duration, repeat, reflect, and reversed properties.
		* <br/><br/>
		* For example, a tween with a position
		* of 5 on a tween with a duration of 3, and repeat set to true would have a tweenPosition of 2 (2 seconds into the first repeat).
		* The same tween with reflect set to true would have a tweenPosition of 1 (because it would be 2 seconds into the first repeat which is
		* playing backwards). With reflect and reversed set to true it would have a tweenPosition of 2.
		* <br/><br/>
		* Tweens with a position less than 0 will have a tweenPosition of 0. Tweens with a position greater than <code>duration*(repeat+1)</code>
		* (the total length of the tween) will have a tweenPosition equal to duration.
		**/
		public function get tweenPosition():Number {
			return _tweenPosition;
		}
		
		/**
		* The target object to tween. This can be any kind of object.
		**/
		public function get target():Object {
			return _target;
		}
		public function set target(value:Object):void {
			_propertyTarget = _target = (value === null) ? {} : value;
			inited = false;
		}
		
		/**
		* Returns the object that will have its property tweened. In a standard GTween, this will usually be the same as target, except if an assignmentTarget was set.
		* This also makes it easy for subclasses like GTweenFilter can divert the property target.
		**/
		public function get propertyTarget():Object {
			return _propertyTarget;
		}
		
		/**
		* Indicates whether a tween should run in reverse. In the simplest examples this means that the tween will play from its end values to its start values.
		* See "reflect" for more information on how these two related properties interact. Also see reverse().
		**/
		public function get reversed():Boolean {
			return _reversed;
		}
		public function set reversed(value:Boolean):void {
			if (value == _reversed) { return; }
			_reversed = value;
			// we force an init so that it jumps to the proper position immediately without flicker.
			if (!inited) { init(); }
			setPosition(_position,true);
		}
		
		/**
		* Returns the current positional state of the tween. This does not indicate if the tween is paused - use the .paused property for this. Possible values are: <code>
		* GTween.START, GTween.DELAY, GTween.TWEEN, GTween.END</code><br/>
		* The beginning state indicates the tween either has not been played. The tween's position will equal -delay.<br/>
		* The delayPhase state indicates that the tween is active (running), but is currently delaying prior to initing. The tween's position is less than 0. Note that it may be paused.<br/>
		* The tweenPhase state indicates that the tween has inited, and is tweening the property values. Note that it may be paused.<br/>
		* The end state indicates that the tween has completed playing. Setting any new properties on the tween will reset it, and set its state to beginning.
		* <br/><br/>
		* New tweens with autoplay set to false start with a state of START. When first played, a tween will have a state of either DELAY (if delay > 0) or TWEEN (if delay == 0).
		* When the delay ends, and tweening begins, the state will change to TWEEN. When the tween reaches its end <code>position == duration*(repeat+1)</code>, the state will be set to END. If you change any end
		* properties on an ended tween, its state will be set back to START.
		**/
		public function get state():String {
			return (_position == -_delay && _paused) ? START : (_position < 0) ? DELAY : (repeat != -1 && _position >= (repeat+1)*duration) ? END : TWEEN;
		}
		
		/**
		* The length of the delay in frames or seconds (depending on the timingMode). The delay occurs before a tween reads initial values or starts playing.
		**/
		public function get delay():Number {
			return _delay;
		}
		public function set delay(value:Number):void {
			if (_position == -_delay) {
				setPosition(-value);
			}
			_delay = value;
		}
		
		/**
		* If set to true, this prevents the tween from reinitializing its start properties automatically (ex. when end properties change).
		* If start properties have not already been initialized, this will also cause the tween to immediate initialize them.
		* Note that this will prevent new start property values from being initialized when invalidating, so it could cause unexpected behaviour
		* if you modify the tween while it is playing.
		**/
		public function get lockStartProperties():Boolean {
			return _lockStartProperties;
		}
		public function set lockStartProperties(value:Boolean):void {
			if (value && !inited) { init(); }
			_lockStartProperties = value;
		}
		
	// public methods:
		
		/**
		* Shorthand method for making multiple setProperty calls quickly. This removes any existing target property values on the tween.
		* <br/><br/>
		* <b>Example:</b> set x and y end values:<br/>
		* <code>myGTween.setProperties({x:200, y:400});</code>
		*
		* @param properties An object containing end property values.
		**/
		public function setProperties(properties:Object):void {
			endValues = {};
			for (var n:String in properties) {
				setProperty(n, properties[n]);
			}
		}
		
		/**
		* Sets the numeric end value for a property on the target object that you would like to tween.
		* For example, if you wanted to tween to a new x position, you could use: myGTween.setProperty("x",400). Non-numeric values are ignored.
		*
		* @param name The name of the property to tween.
		* @param value The numeric end value (the value to tween to).
		**/
		public function setProperty(name:String, value:Number):void {
			if (isNaN(value)) { return; }
			endValues[name] = value;
			if (_lockStartProperties && startValues[name] == null) {
				startValues[name] = _propertyTarget[name];
			}
			invalidate();
		}
		
		/**
		* Returns the end value for the specified property if one exists.
		*
		* @param name The name of the property to return a end value for.
		**/
		public function getProperty(name:String):Number {
			return endValues[name];
		}
		
		/**
		* Removes a end value from the tween. This prevents the GTween instance from tweening the property.
		*
		* @param name The name of the end property to delete.
		**/
		public function deleteProperty(name:String):Boolean {
			return delete(endValues[name]);
		}
		
		/**
		* Returns the hash table of all end properties and their values. This is a copy of the internal hash of values, so modifying
		* the returned object will not affect the tween.
		**/
		public function getProperties():Object {
			return copyObject(endValues);
		}
		
		/**
		* Allows you to manually assign the start property values for a tween. These are the properties that will be applied when the tween
		* is at tween position 0. Normally these are automatically copied from the target object on initialization, or whenever a end value
		* changes. You can also use the lockStartProperties property to ensure your start properties are not reinitialized after you set them.
		*
		* @param properties An object containing start property values.
		**/
		public function setStartProperties(properties:Object):void {
			startValues = copyObject(properties);
			inited = true;
		}
		
		/**
		* Returns the hash table of all start properties and their values. This is a copy of the internal hash of values, so modifying
		* the returned object will not affect the tween.
		**/
		public function getStartProperties():Object {
			return copyObject(startValues);
		}
		
		/**
		* Shortcut method for setting multiple properties on the tween instance quickly. This does not set end values (ie. the value to tween to).
		* This method also provides you with a quick method for adding listeners to specific events, using the special properties:
		* initListener, completeListener, changeListener.
		* <br/><br/>
		* <b>Example:</b> This will set the duration, reflect, and nextTween properties of a tween, and add a listener for the complete event:<br/>
		* <code>myTween.setTweenProperties({duration:4, reflect:true, nextTween:anotherTween, completeListener:completeHandlerFunction});</code>
		**/
		public function setTweenProperties(properties:Object):void {
			if (!properties) { return; }
			
			var positionValue:Number;
			if ("position" in properties) { positionValue = properties.position; delete(properties.position); }
			if ("initListener" in properties) { addEventListener(Event.INIT,properties.initListener,false,0,true); delete(properties.initListener); }
			if ("completeListener" in properties) { addEventListener(Event.COMPLETE,properties.completeListener,false,0,true); delete(properties.completeListener); }
			if ("changeListener" in properties) { addEventListener(Event.CHANGE,properties.changeListener,false,0,true); delete(properties.changeListener); }
			
			for (var n:String in properties) {
				this[n] = properties[n];
			}
			
			if (!isNaN(positionValue)) { position = positionValue; }
		}
		
		/**
		* Toggles the reversed property and inverts the current tween position.
		* This will cause a tween to reverse playing visually.
		* There is currently an issue with this functionality for tweens with a repeat of -1
		*
		* @param suppressEvents Indicates whether to suppress any events or callbacks that are generated as a result of the position change.
		**/
		public function reverse(suppressEvents:Boolean=true):void {
			var pos:Number = repeat == -1 ? duration-_position%duration : (repeat+1)*duration-_position;
			if (reflect) {
				_reversed = ((position/duration%2>=1) == (pos/duration%2>=1)) != _reversed;
			} else {
				_reversed = !_reversed;
			}
			setPosition(pos,suppressEvents);
		}
		
		/**
		* Invalidate forces the tween to repopulate all of the initial properties from the target object, and start playing if autoplay is set to true.
		* If the tween is currently playing, then it will also set the position to 0. For example, if you changed the x and y position of a the target
		* object while the tween was playing, you could call invalidate on it to force it to resume the tween with the new property values.
		**/
		public function invalidate():void {
			inited = false;
			if (_position > 0) {
				_position = 0;
				updatePositionOffset();
			}
			if (autoPlay) { paused = false; }
		}
		
		/**
		* Pauses the tween by stopping tick from being automatically called. This also releases the tween for garbage collection if
		* it is not referenced externally.
		**/
		public function pause():void {
			paused = true;
		}
		
		/**
		* Plays a tween by incrementing the position property each frame. This also prevents the tween from being garbage collected while it is active.
		* This is achieved by way of two methods:<br/>
		* 1. If the target object is an IEventDispatcher, then the tween will subscribe to a dummy event using a hard reference. This allows
		* the tween to be garbage collected if its target is also collected, and there are no other external references to it.<br/>
		* 2. If the target object is not an IEventDispatcher, then the tween is placed in the activeTweens list, to prevent collection until it is paused or reaches the end of the transition).
		* Note that pausing all tweens via the GTween.pauseAll static property will not free the tweens for collection.
		**/
		public function play():void {
			paused = false;
		}
		
		/**
		* Jumps the tween to its beginning. This is the same as setting <code>position=-delay</code>.
		**/
		public function beginning():void {
			setPosition(-_delay);
		}
		
		/**
		* Jumps the tween to its end. This is the same as setting <code>position=(repeat+1)*duration</code>.
		**/
		public function end():void {
			setPosition( (repeat == -1) ? duration : (repeat+1)*duration );
		}
		
		/**
		* Allows you to tween objects that require re-assignment whenever they are modified by reassigning the target object to a specified property of another
		* object. For example, in order for changes to a colorTransform object to be visible,
		* it must be assigned back to the <code>.transform.colorTransform</code> property of a display object. To make this work, you would call
		* <code>myTween.setAssignment(myDisplayObject.transform,"colorTransform");</code>
		* This will also cause GTween to retrieve the target each time it copies its initial values.
		* <br/><br/>
		* <b>Note:</b> this does not work with filters, as they must be assigned to an array first, and then to the filters property. Use GTweenFilter instead.
		*
		* @param assignmentTarget The object to reassign the property on.
		* @param assignmentProperty The name of the property to reassign the target to.
		**/
		public function setAssignment(assignmentTarget:Object=null, assignmentProperty:String=null):void {
			this.assignmentTarget = assignmentTarget;
			this.assignmentProperty = assignmentProperty;
			inited = false;
		}
		
		/**
		* Sets the position of the tween. Using the position property will always suppress events and callbacks, whereas the
		* setPosition method allows you to manually set the position and specify whether to suppress events or not.
		*
		* @param value The position to jump to in seconds or frames (depending on the timingMode).
		* @param suppressEvents Indicates whether to suppress events and callbacks generated from the change in position.
		**/
		public function setPosition(position:Number,suppressEvents:Boolean=true):void {
			_previousPosition = _position;
			_position = position;
			if (!inTick && !paused) { updatePositionOffset(); }
			var maxPos:Number = (repeat+1)*duration;
			var tp:Number;
			if (position < 0) {
				tp = _reversed ? duration : 0;
			} else if (repeat == -1 || position < maxPos) {
				tp = position%duration;
				if ((reflect && position/duration%2>=1) != _reversed) { tp = duration-tp; }
			} else {
				tp = ((reflect && repeat%2>=1) != _reversed) ? 0 : duration;
			}
			if (tp == _tweenPosition) { return; }
			_previousTweenPosition = _tweenPosition;
			_tweenPosition = tp;
			
			if (!suppressEvents && hasEventListener(Event.CHANGE)) { dispatchEvent(new Event(Event.CHANGE)); }
			
			
			if (!inited && _previousPosition <= 0 && _position >= 0) {
				init();
				if (!suppressEvents && hasEventListener(Event.INIT)) { dispatchEvent(new Event(Event.INIT)); }
			}
			
			updateProperties();
			
			if (repeat != -1 && _previousPosition < maxPos && position >= maxPos) {
				if (!suppressEvents && hasEventListener(Event.COMPLETE)) { dispatchEvent(new Event(Event.COMPLETE)); }
				paused = true;
				if (nextTween) { nextTween.paused = false; }
			}
		}
		
	// private methods
		// copies the initial target properties into the local startValues store.
		/** @private **/
		protected function init():void {
			inited = true;
			if (_lockStartProperties) { return; }
			startValues = {};
			if (assignmentTarget && assignmentProperty) { _propertyTarget = assignmentTarget[assignmentProperty]; }
			for (var n:String in endValues) {
				if (autoRotation && rotationProperties[n]) {
					var r:Number = endValues[n] = endValues[n] %360;
					var tr:Number = _propertyTarget[n] %360;
					startValues[n] = tr + ((Math.abs(tr-r) < 180) ? 0 : (tr>r) ? -360 : 360);
				} else {
					startValues[n] = _propertyTarget[n];
				}
			}
		}
		
		// logic that runs each frame. Calculates eased position, updates properties, and reassigns the target if an assignmentTarget was set.
		/** @private **/
		protected function updateProperties():void {
			var ratio:Number = ease(_tweenPosition/duration, 0, 1, 1);
			for (var n:String in endValues) {
				updateProperty(n,startValues[n],endValues[n],ratio);
			}
			if (autoVisible && "alpha" in endValues && "alpha" in _propertyTarget && "visible" in _propertyTarget) { _propertyTarget.visible = _propertyTarget.alpha > 0; }
			if (assignmentTarget && assignmentProperty) { assignmentTarget[assignmentProperty] = _propertyTarget; }
		}
		
		// updates a single property. Mostly for overriding.
		/** @private **/
		protected function updateProperty(property:String, startValue:Number, endValue:Number, tweenRatio:Number):void {
			var value:Number = startValue+(endValue-startValue)*tweenRatio;
			if (snapping && snappingProperties[property]) { value = Math.round(value); }
			if (property == "currentFrame") { _propertyTarget.gotoAndStop(value<<0); }
			else { _propertyTarget[property] = value; }
		}
		
		// locks or unlocks the tween in memory.
		/** @private **/
		protected function setGCLock(value:Boolean):void {
			if (value) {
				if (_target is IEventDispatcher) { _target.addEventListener("GDS__NONEXISTENT_EVENT", nullListener,false,0,false); }
				else { activeTweens[this] = true; }
			} else {
				if (_target is IEventDispatcher) { _target.removeEventListener("GDS__NONEXISTENT_EVENT", nullListener); }
				delete(activeTweens[this]);
			}
		}
		
		// copies an object's dynamic properties.
		/** @private **/
		protected function copyObject(o:Object):Object {
			var copy:Object = {};
			for (var n:String in o) {
				copy[o] = o[n];
			}
			return copy;
		}
		
		// updates the current positionOffset based on the current ticker position.
		/** @private **/
		protected function updatePositionOffset():void {
			positionOffset = ticker.position-_position;
		}
		
		// empty listener used by setGCLock.
		/** @private **/
		protected function nullListener(evt:Event):void {};
		
		// handles tick events while playing.
		/** @private **/
		protected function handleTick(evt:Event):void {
			inTick = true;
			if (pauseAll) { updatePositionOffset(); }
			else { setPosition(ticker.position - positionOffset, false); }
			inTick = false;
		}
		
		
	}
	
}



import flash.events.IEventDispatcher;
interface ITicker extends IEventDispatcher {
	function get position():Number;
}

import flash.utils.getTimer;
import flash.utils.Timer;
import flash.events.EventDispatcher;
import flash.events.Event;
import flash.events.TimerEvent;
class TimeTicker extends EventDispatcher implements ITicker {
	
	protected var timer:Timer;
	
	public function TimeTicker():void {
		timer = new Timer(20);
		timer.start();
		timer.addEventListener(TimerEvent.TIMER,tick);
	}
	
	public function get position():Number {
		return getTimer()/1000;
	}
	
	public function set interval(value:Number):void {
		timer.delay = value*1000;
	}
	
	protected function tick(evt:TimerEvent):void {
		dispatchEvent(new Event("tick"));
		evt.updateAfterEvent();
	}
}

import flash.display.Shape;
class FrameTicker extends EventDispatcher implements ITicker {
	
	protected var shape:Shape;
	protected var _position:Number=0;
	
	public function FrameTicker():void {
		shape = new Shape();
		shape.addEventListener(Event.ENTER_FRAME,tick);
	}
	
	public function get position():Number {
		return _position;
	}
	
	protected function tick(evt:Event):void {
		_position++;
		dispatchEvent(new Event("tick"));
	}
}

class HybridTicker extends EventDispatcher implements ITicker {
	
	protected var shape:Shape;
	
	public function HybridTicker():void {
		shape = new Shape();
		shape.addEventListener(Event.ENTER_FRAME,tick);
	}
	
	public function get position():Number {
		return getTimer()/1000;
	}
	
	protected function tick(evt:Event):void {
		dispatchEvent(new Event("tick"));
	}
}

import flash.utils.Proxy;
import flash.utils.flash_proxy;
import com.gskinner.motion.GTween;
dynamic class TargetProxy extends Proxy {
	
	private var gTween:GTween;
	
	public function TargetProxy(gTween:GTween):void {
		this.gTween = gTween;
	}
	
// proxy methods:
	flash_proxy override function callProperty(methodName:*, ...args:Array):* {
		return gTween.propertyTarget[methodName].apply(null,args); // GDS: propertyTarget.
	}
	
	flash_proxy override function getProperty(prop:*):* {
		var value:Number = gTween.getProperty(prop);
		return (isNaN(value)) ? gTween.propertyTarget[prop] : value;
	}
	
	flash_proxy override function setProperty(prop:*,value:*):void {
		if (isNaN(value)) { gTween.propertyTarget[prop] = value; }
		else { gTween.setProperty(String(prop), Number(value)); }
	}
	
	flash_proxy override function deleteProperty(prop:*):Boolean {
		return gTween.deleteProperty(prop);
	}
}